
#include <BStyle>
#include <BImage>
#include <BReadWrite>

#include <BRender>
#include <BMesh>
#include <BCube>
#include <BCone>
#include <BSphere>
#include <BGeoSphere>
#include <BCylinder>
#include <BCapsule>
#include <BSurface>
#include <BMaterial>
#include <BBillboard>
#include <BLight>
#include <BRoundHandler>

#include <BHBoxLayout>
#include <BVBoxLayout>
#include <BGridLayout>
#include <BDockLayout>
#include <BMouseEvent>
#include <BLabel>
#include <BSpacer>
#include <BGroupBox>

#include "PixmapWindow.h"
#include "MainWindow.h"

MainWindow::MainWindow(const BString& title, BWidget* parent) : BMainWindow(title, parent)
{
	BScene* scene = createScene();

	BCameraHolder camera = new BCamera();
	camera->setPosition(0, -200, 100);
	camera->setCenter(0, 0, 0);
	camera->setNear((BReal)0.1);
	camera->setFar(1000);
	camera->setColor(10, 30, 66, 100);

	BHandler* handler = new TargetHandler();
	{
		BGeometry* screen = (BGeometry*)scene->shape("screen");
		PixmapHandler* pixmapHandler = new PixmapHandler(screen);
		handler->addChild(pixmapHandler);
	}

	BViewer* viewer = new BViewer();
	viewer->setScene(scene);
	viewer->setCamera(camera);
	viewer->setHandler(handler);
	viewer->setBorder(10);
	viewer->setPerch(10);
	viewer->setMargin(10);

	BGroupBox* positionGroupBox = new BGroupBox("Position");
	positionGroupBox->setTips("Position of selected object");
	positionGroupBox->setIcon(new BIcon("data/icon/check/checkbox"));
	positionGroupBox->setTitleAlign(Align_Center);
	positionGroupBox->setCheckable(true);
	positionGroupBox->setChecked(true);
	{
		BLabel* posXLabel = new BLabel("pos X");
		_posXSpinBox = new BDoubleSpinBox();

		BLabel* posYLabel = new BLabel("pos Y");
		_posYSpinBox = new BDoubleSpinBox();

		BLabel* posZLabel = new BLabel("pos Z");
		_posZSpinBox = new BDoubleSpinBox();

		BGridLayout* gridLayout = new BGridLayout(positionGroupBox);
		gridLayout->setColumnStretch(0, 0);
		gridLayout->addWidget(0, 0, posXLabel);
		gridLayout->addWidget(0, 1, _posXSpinBox);
		gridLayout->addWidget(1, 0, posYLabel);
		gridLayout->addWidget(1, 1, _posYSpinBox);
		gridLayout->addWidget(2, 0, posZLabel);
		gridLayout->addWidget(2, 1, _posZSpinBox);
	}

	BDockLayout* dockLayout = new BDockLayout(viewer);
	dockLayout->addWidget(positionGroupBox, Align_RightTop);
	dockLayout->setSplitable(true);
	dockLayout->setSpacing(10);

	BVBoxLayout* rightLayout = new BVBoxLayout();
	{
		BWidget* shapeWidget = createShapeWidget();
		_stackLayout = new BStackLayout();
		{
			BWidget* cubeWidget = createCubeWidget();
			BWidget* capsuleWidget = createCapsuleWidget();
			_stackLayout->addWidget(cubeWidget, Align_RightBottom);
			_stackLayout->addWidget(capsuleWidget);
		}
		rightLayout->addWidget(shapeWidget);
		rightLayout->addLayout(_stackLayout);
		rightLayout->addSpring();
		rightLayout->setSpacing(3);
	}

	BHBoxLayout* hlayout = new BHBoxLayout(this);
	hlayout->addWidget(viewer);
	hlayout->addLayout(rightLayout, 0);
	hlayout->setSplitable(true);
	hlayout->setSpacing(5);

	connect("target-changed", &MainWindow::slotTargetChanged);
	connect(positionGroupBox, Signal_DoubleClicked, &MainWindow::slotGroupBoxDoubleClicked);
	connect(positionGroupBox, Signal_Checked, &MainWindow::slotPositionGroupBoxChecked);

	_stackLayout->setCurrent(0);
}
MainWindow::~MainWindow()
{

}

BGeometry* MainWindow::createScreen()
{
	BVectorArray* vertices = new BVectorArray(4);
	vertices->set(0, BVector(0, 0, 0));
	vertices->set(1, BVector(192, 0, 0));
	vertices->set(2, BVector(192, 0, 108));
	vertices->set(3, BVector(0, 0, 108));

	BVectorArray* normals = new BVectorArray(4);
	normals->set(0, BVector(0, -1, 0));
	normals->set(1, BVector(0, -1, 0));
	normals->set(2, BVector(0, -1, 0));
	normals->set(3, BVector(0, -1, 0));

	BCoordArray* texCoords = new BCoordArray(4);
	texCoords->set(0, BCoord(0, 0));
	texCoords->set(1, BCoord(1, 0));
	texCoords->set(2, BCoord(1, 1));
	texCoords->set(3, BCoord(0, 1));

	BMaterial* material = new BMaterial;
	material->setAmbient(BColor(136, 136, 136));
	material->setDiffuse(BColor(255, 255, 255));
	material->setSpecular(BColor(222, 222, 222));
	material->setShininess(100);

	BPixmap* pixmap = new BPixmap(1920, 1080);
	{
		BWidget* widget = new PixmapWindow("Inline Window");
		widget->setBorder(10);
		pixmap->addWidget(widget);
		pixmap->setColor(BColor(200, 160, 200, 180));
		pixmap->setBlend(BTexture::Blend_Replace);
	}
	//pixmap->setEnabled(false);

	BGeometry* geometry = new BGeometry();
	geometry->setName("screen");
	geometry->addElement(Primitive_Quads, 0, 4);
	geometry->setVertices(vertices);
	geometry->setNormals(normals);
	geometry->setTexCoords(texCoords);
	geometry->setMaterial(material);
	geometry->setMaterial(material);
	geometry->setTexture(pixmap);

	return geometry;
}
BLight* MainWindow::createLight()
{
	BLight* light = new BLight();
	light->setAmbient(36, 36, 36);
	light->setDiffuse(200, 200, 200);
	light->setSpecular(255, 200, 0);

	BImage* image = new BImage("data/picture/particlewhite.bmp");
	if (image->format() == Format_RGB)
	{
		BByte* bytes = image->pixels()->data();
		int count = image->size().width() * image->size().height();
		BByteArray* pixels = new BByteArray(count * sizeof(BColor));
		BColor* colors = (BColor*)pixels->data();
		for (int i = 0; i < count; i++)
		{
			int pos = i * 3;
			BByte r = bytes[pos + 0];
			BByte g = bytes[pos + 1];
			BByte b = bytes[pos + 2];
			BByte a = bMax(bMax(r, g), b);
			colors[i].set(r, g, b, a);
		}
		image->setFormat(Format_RGBA);
		image->setPixels(pixels);
	}


	BBillboard* billboard = new BBillboard(100, 100);
	billboard->setName("billboard");
	billboard->setTexture(image);
	billboard->setTransparent(true);
	billboard->setFacing(BBillboard::Facing_Axial);

	light->insertShape(billboard);
	return light;
}
BScene* MainWindow::createScene()
{
	BSurface* ground = new BSurface(500, 500, 20, 20);
	ground->setName("Ground");
	ground->setOrientation(Orientation_Vertical);
	ground->setPosition(-150, -150, 0);
	ground->setCullback(false);
	{
		BImage* image = ReadFile<BImage>("data/picture/whitemetal_diffuse.jpg");
		if (image)
		{
			image->setBlend(BTexture::Blend_Modulate);
			image->setFilter(BTexture::Filter_Linear);
		}
		ground->setTexture(image);

		BMaterial* material = new BMaterial(BColor(255, 255, 255));
		material->setSpecular(255, 255, 255);
		material->setShininess(56);
		ground->setMaterial(material);
	}

	BImage* image = ReadFile<BImage>("data/picture/land_shallow_topo_2048.jpg");
	if (image)
	{
		image->setBlend(BTexture::Blend_Modulate);
		image->setFilter(BTexture::Filter_Linear);
	}

	BCube* cube = new BCube(30, 3);
	cube->setName("cube");
	cube->setPosition(200, 200, 30);
	cube->setTexture(image);

	BCone* cone = new BCone(15, 80, 12, 10);
	cone->setPosition(200, 100, 20);
	cone->setTexture(image);
	//cone->setPolyMode(Poly_Line);

	BCylinder* cylinder = new BCylinder(30, 50, 66, 10, 6);
	{
		BMaterial* material = new BMaterial(BColor(255, 255, 255));
		cylinder->setName("cylinder");
		cylinder->setTexture(image);
		cylinder->setMaterial(material);
		cylinder->setPosition(160, 0, 25);
	}

	BCapsule* capsule = new BCapsule(30, 50, 66, 10, 10);
	{
		capsule->setPosition(260, -100, 55);
		BMaterial* material = new BMaterial(BColor(255, 255, 255));
		capsule->setName("capsule");
		capsule->setMaterial(material);
		BTexture* texture = new BImage("data/picture/flower.jpg");
		texture->setBlend(BTexture::Blend_Modulate);
		capsule->setTexture(texture);
	}

	BSphere* sphere = new BSphere(30, 128, 64);
	{
		sphere->setName("sphere");
		sphere->setTexture(image);
		sphere->setPosition(-100, -50, 30);
	}

	BGeoSphere* geosphere = new BGeoSphere(50, 3);
	{
		geosphere->setName("geosphere");
		geosphere->setPolyMode(Poly_Line);
		geosphere->setLineWidth(2);
		geosphere->setLineSmooth(true);
		geosphere->setTexture(image);
		geosphere->setPosition(0, 130, 50);
	}

	BGeometry* geometry = createScreen();
	geometry->setPosition(-96, 0, 0);
	geometry->setLighting(false);
	geometry->setCullback(false);
	geometry->setTransparent(true);

	BLight* light = createLight();
	light->setPosition(0, 0, 200);

	BScene* scene = new BScene();
	scene->insert(ground);
	scene->insert(cube);
	scene->insert(cone);
	scene->insert(cylinder);
	scene->insert(capsule);
	scene->insert(sphere);
	scene->insert(geosphere);
	scene->insert(geometry);
	scene->insert(light);

	return scene;
}

BWidget* MainWindow::createShapeWidget()
{
	BLabel* nameLabel = new BLabel("name");
	_nameEdit = new BLineEdit();

	BLabel* visibleLabel = new BLabel("visible");
	_visibleCheckBox = new BCheckBox();

	BLabel* polyModeLabel = new BLabel("polymode");
	_polymodeComboBox = new BComboBox();
	_polymodeComboBox->addItem(Graph_Circle, "Poly_None", Poly_None);
	_polymodeComboBox->addItem(Graph_Circle, "Poly-Fill", Poly_Fill);
	_polymodeComboBox->addItem(Graph_Circle, "Poly_Line", Poly_Line);
	_polymodeComboBox->addItem(Graph_Circle, "Poly_Point", Poly_Point);
	_polymodeComboBox->setCurrentIndex(0);
	_polymodeComboBox->setAlign(Align_Center);
	_polymodeComboBox->setForm(Form_TextUnderIcon);

	BGroupBox* groupBox = new BGroupBox("Shape");
	BGridLayout* gridLayout = new BGridLayout(groupBox);
	gridLayout->setSizePolicy(Policy_Preferred | Policy_Dynamic, Policy_Preferred);
	gridLayout->addWidget(0, 0, nameLabel);
	gridLayout->addWidget(0, 1, _nameEdit);
	gridLayout->addWidget(1, 0, visibleLabel);
	gridLayout->addWidget(1, 1, _visibleCheckBox);
	gridLayout->addWidget(2, 0, polyModeLabel);
	gridLayout->addWidget(2, 1, _polymodeComboBox);
	gridLayout->setColumnStretch(0, 1);
	gridLayout->setColumnStretch(1, 10);
	connect(groupBox, Signal_DoubleClicked, &MainWindow::slotGroupBoxDoubleClicked);
	return groupBox;
}
BWidget* MainWindow::createCubeWidget()
{
	BLabel* lengthLabel = new BLabel("Length");
	_cubeLengthSpinBox = new BRealSpinBox;

	BLabel* widthLabel = new BLabel("Width");
	_cubeWidthSpinBox = new BRealSpinBox();

	BLabel* heightLabel = new BLabel("Height");
	_cubeHeightSpinBox = new BRealSpinBox();

	BGroupBox* groupBox = new BGroupBox("Cube");
	BGridLayout* gridLayout = new BGridLayout(groupBox);
	gridLayout->addWidget(0, 0, lengthLabel);
	gridLayout->addWidget(0, 1, _cubeLengthSpinBox);
	gridLayout->addWidget(1, 0, widthLabel);
	gridLayout->addWidget(1, 1, _cubeWidthSpinBox);
	gridLayout->addWidget(2, 0, heightLabel);
	gridLayout->addWidget(2, 1, _cubeHeightSpinBox);
	connect(groupBox, Signal_DoubleClicked, &MainWindow::slotGroupBoxDoubleClicked);
	return groupBox;
}
BWidget* MainWindow::createCapsuleWidget()
{
	BLabel* radiusLabel = new BLabel("Radius");
	_capsuleRadiusSpinBox = new BRealSpinBox;

	BLabel* heightLabel = new BLabel("Height");
	_capsuleHeightSpinBox = new BRealSpinBox();

	BLabel* sidesLabel = new BLabel("Sides");
	_capsuleSidesSpinBox = new BSpinBox();

	BGroupBox* groupBox = new BGroupBox("Capsule");
	BGridLayout* gridLayout = new BGridLayout(groupBox);
	gridLayout->addWidget(0, 0, radiusLabel);
	gridLayout->addWidget(0, 1, _capsuleRadiusSpinBox);
	gridLayout->addWidget(1, 0, heightLabel);
	gridLayout->addWidget(1, 1, _capsuleHeightSpinBox);
	gridLayout->addWidget(21, 0, sidesLabel);
	gridLayout->addWidget(21, 1, _capsuleSidesSpinBox);
	connect(groupBox, Signal_DoubleClicked, &MainWindow::slotGroupBoxDoubleClicked);
	return groupBox;
}

void MainWindow::slotTargetChanged(BObject* object, const BValue& value)
{
	if (BShape* shape = value)
	{
		_nameEdit->setText(shape->name());
		_visibleCheckBox->setChecked(shape->visible());
		_polymodeComboBox->setValue(shape->polyMode());
		BVector position = shape->position();
		_posXSpinBox->setValue(position.x());
		_posYSpinBox->setValue(position.y());
		_posZSpinBox->setValue(position.z());
	}
	else
	{
		_nameEdit->setText(BString());
		_visibleCheckBox->setChecked(false);
		_polymodeComboBox->setValue(Policy_None);
		_posXSpinBox->setValue(0);
		_posYSpinBox->setValue(0);
		_posZSpinBox->setValue(0);
	}
	if (BCube* cube = value)
	{
		_cubeLengthSpinBox->setValue(cube->length());
		_cubeWidthSpinBox->setValue(cube->width());
		_cubeHeightSpinBox->setValue(cube->height());
		_stackLayout->setCurrentIndex(0);
		return;
	}
	if (BCapsule* capsule = value)
	{
		_capsuleRadiusSpinBox->setValue(capsule->radius());
		_capsuleHeightSpinBox->setValue(capsule->height());
		_capsuleSidesSpinBox->setValue(capsule->sides());
		_stackLayout->setCurrentIndex(1);
		return;
	}
	_stackLayout->setCurrentIndex(-1);
}

void MainWindow::slotPositionGroupBoxChecked(BWidget* widget, const BValue& value)
{
	bool checked = value;
	for (int i = 0; i < widget->childCount(); i++)
	{
		widget->child(i)->setEnabled(checked);
	}
}
void MainWindow::slotGroupBoxDoubleClicked(BWidget* widget, const BValue& value)
{
	BGroupBox* groupBox = (BGroupBox*)widget;
	groupBox->setFolded(!groupBox->folded());
}

void MainWindow::styleEvent(const BEvent& event)
{
	BMainWindow::styleEvent(event);
	const BStyle* style = event.value();
	for (int i = 0; i < _polymodeComboBox->itemCount(); i++)
	{
		BComboItem* item = _polymodeComboBox->item(i);
		BString text = item->text();
		text.lower();
		text.replace('_', '-');
		item->setImage(style->image(text));
	}
}
